#include "muduo/prot/codec.h"
#include "muduo/prot/dispatcher.h"
#include "request.pb.h"

#include "muduo/base/Logging.h"
#include "muduo/base/Mutex.h"
#include "muduo/net/EventLoopThread.h"
#include "muduo/net/TcpClient.h"
#include "muduo/net/TcpConnection.h"

#include <iostream>
#include <string>

class Client
{
public:
    using TranslateResponsePtr = std::shared_ptr<xg::TranslateResponse>;
    using AddResponsePtr = std::shared_ptr<xg::AddResponse>;

public:
    Client(const std::string &ip, int port)
        : _client(_baseloop.startLoop(), muduo::net::InetAddress("127.0.0.1", 8081), "client"), _dispatcher(std::bind(&Client::onUnknownMessage, this, std::placeholders::_1,
                                                                                                                      std::placeholders::_2, std::placeholders::_3)),
          _codec(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher,
                           std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)),
          _latch(1)
    {
        // 给分发器注册方法,这里需要指明类型
        _dispatcher.registerMessageCallback<xg::AddResponse>(std::bind(&Client::OnAdd, this, std::placeholders::_1,
                                                                       std::placeholders::_2, std::placeholders::_3));
        _dispatcher.registerMessageCallback<xg::TranslateResponse>(std::bind(&Client::OnTranslate, this, std::placeholders::_1,
                                                                             std::placeholders::_2, std::placeholders::_3));

        // 给服务器设置回调函数
        // 收到消息后的回调函数
        _client.setMessageCallback(std::bind(&ProtobufCodec::onMessage, &_codec, std::placeholders::_1,
                                             std::placeholders::_2, std::placeholders::_3));
        // 连接变化的回调函数
        _client.setConnectionCallback(std::bind(&Client::OnConnection, this, std::placeholders::_1));
    }
    // 连接服务器
    void connect()
    {
        _client.connect(); // 与服务器建立连接
        _latch.wait();     // 在这里阻塞，直到建立连接成功
    }
    //给外界提供的两个接口
    void Add(int num1,int num2)
    {
        //构建请求
        xg::AddRequest req;
        req.set_num1(num1);
        req.set_num2(num2);

        send(&req);
    }
    void Translate(const std::string& msg)
    {
        //构建请求
        xg::TranslateRequest req;
        req.set_msg(msg);

        send(&req);
    }
private:
    void OnAdd(const muduo::net::TcpConnectionPtr &conn,
               const AddResponsePtr &message,
               muduo::Timestamp)
    {
        // 收到响应后要进行的操作
        std::cout<<"加法结果为: "<<message->result()<<std::endl;
    }
    void OnTranslate(const muduo::net::TcpConnectionPtr &conn,
                     const TranslateResponsePtr &message,
                     muduo::Timestamp)
    {
        std::cout<<"翻译结果为: "<<message->msg()<<std::endl;
    }

    // 通过该接口发送
    bool send(const google::protobuf::Message* message)
    {
        // muduo中的操作都是异步的，需要判断连接是否任然存在
        if (_conn->connected())
        {
            _codec.send(_conn,*message);
            return true;
        }
        return false;
    }
    // 新连接建立成功的回调函数
    void OnConnection(const muduo::net::TcpConnectionPtr &conn)
    {
        if (conn->connected())
        {
            _latch.countDown(); // 唤醒阻塞的主线程
            _conn = conn;
        }
        else
        {
            // 重置连接
            _conn.reset();
        }
    }
    void onUnknownMessage(const muduo::net::TcpConnectionPtr &conn,
                          const MessagePtr &message,
                          muduo::Timestamp)
    {
        LOG_INFO << "onUnknownMessage: " << message->GetTypeName();
        conn->shutdown();
    }

private:
    muduo::net::EventLoopThread _baseloop; // 事件循环
    muduo::net::TcpConnectionPtr _conn;    // 连接管理
    muduo::net::TcpClient _client;         // 客户端
    muduo::CountDownLatch _latch;          // 实现同步
    ProtobufDispatcher _dispatcher;        // 请求分发器对象，每一种请求对应一个回调方法
    ProtobufCodec _codec;                  // protobuf协议处理器，将收到的消息通过协议 进行序列化和反序列化
    // 再通过分发器完成业务层面的处理
};

int main()
{
    Client clinet("127.0.0.1",8081);
    clinet.connect();

    clinet.Add(11,22);
    clinet.Translate("ILoveYou");

    sleep(1);
    return 0;
}